package com.studio5704website.infrastructure.service.cusjwtservice;

import com.alibaba.fastjson.JSON;
import com.studio5704website.core.service.JwtService;
import com.studio5704website.core.user.User;
import org.apache.logging.log4j.util.Base64Util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.util.Base64Utils;
import javax.crypto.Cipher;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;


/**
 * @author 高威
 * 自定义Jwt服务
 *
 */
@Component
public class CusJwtService implements JwtService {

    private CusJwt cusJwt;

    @Autowired
    public CusJwtService(CusJwt cusJwt) {
        this.cusJwt = cusJwt;
    }
    @Override
    public String toToken(User user) {
        cusJwt.setSubject(user.getId())
                .setIat(System.currentTimeMillis())
                .build();
        return cusJwt.compact();
    }

    @Override
    public Optional<String> getSubFromToken(String token) {
        try {
            return Optional.ofNullable(cusJwt.getSubject(token));
        } catch (UnsupportedCusJwtException e) {
            return Optional.empty();
        }
    }
}

@Service
class CusJwt {
    private Map<String, String> header;
    private Map<String, Object> payload;
    private String subject = "";
    private Long iat = null;
    private String signature = "CusJWT";
    private Cipher encryptCipher;
    private Cipher decryptCipher;

    @Autowired
    public void setEncryptCipher(@Qualifier("encryptCipher") Cipher encryptCipher) {
        this.encryptCipher = encryptCipher;
    }

    @Autowired
    public void setDecryptCipher(@Qualifier("decryptCipher") Cipher decryptCipher) {
        this.decryptCipher = decryptCipher;
    }

    public void build() {
        this.payload.put("sub", this.subject);
        this.payload.put("iss", "5704websiteCusJwt");
        if (iat == null) {
            iat = System.currentTimeMillis();
        }
        this.payload.put("iat", this.iat.toString());
        this.header.put("typ", "CusJWT");
        StringBuilder builder = new StringBuilder();
        builder.append(signature);
        builder.append(" ");
        builder.append(this.iat);
        this.signature = builder.toString();
    }

    private CusJwt() {
        header = new HashMap<>();
        payload = new HashMap<>();
    }

    public String compact() {
        StringBuilder token = new StringBuilder();
        String header = Base64Util.encode(JSON.toJSONString(this.header));
        token.append(header);
        token.append('.');
        token.append(Base64Util.encode(JSON.toJSONString(this.payload)));
        token.append('.');
        try {
            token.append(Base64Utils.encodeToString(encryptCipher.doFinal(signature.getBytes())));
            return token.toString();
        } catch (Exception e) {
            e.printStackTrace();
            token.deleteCharAt(token.length() - 1);
            return token.toString();
        } finally {
            this.header.clear();
            this.payload.clear();
            this.signature = "";
        }
    }

    public String getSubject(String token) {
        String[] str = token.split("\\.");
        if (str.length != 3) {
            throw new UnsupportedCusJwtException();
        } else {
            String str1 = new String(Base64Utils.decodeFromString(str[0]));
            Map<String, String> header = (Map<String, String>) JSON.parse(str1);
            String jwt = header.get("typ");
            if (jwt == null || !jwt.equals("CusJWT")) {
                throw new UnsupportedCusJwtException();
            }

            String str2 = new String(Base64Utils.decodeFromString(str[1]));
            Map<String, String> payload = (Map<String, String>) JSON.parse(str2);
            String sub = payload.get("sub");
            if (sub == null) { throw new UnsupportedCusJwtException(); }
            try {
                String[] str3 = new String(decryptCipher.doFinal(Base64Utils.decodeFromString(str[2]))).split(" ");
                if (str3.length < 2 || !str3[1].equals(payload.get("iat"))) {
                    throw new SignatureException();
                }
            } catch (Exception e) {
                throw new SignatureException();
            }
            return sub;
        }
    }

    public CusJwt setSubject(String subject) {
        this.subject = subject;
        return this;
    }

    public CusJwt setIat(long time) {
        this.iat = time;
        return this;
    }

    public CusJwt signWith(String signature) {
        this.signature = signature;
        return this;
    }
}
